/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Sylvain Sicard - initial API and implementation
 ******************************************************************************/
package fr.jade.service.fm.probe.ping;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.api.control.IllegalContentException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.api.control.LifeCycleController;

import fr.jade.base.JadeBaseComponent;
import fr.jade.event.Event;
import fr.jade.event.EventFactory;
import fr.jade.event.EventHandler;
import fr.jade.event.EventTag;
import fr.jade.fraclite.api.control.NoSuchAttributeException;
import fr.jade.reflex.util.Reflex;
import fr.jade.util.FractalNavigator;
import fr.jade.util.FractalUtil;
import fr.jade.util.JadeException;

public class BasicPing extends JadeBaseComponent implements PingResultHandler, EventHandler, ContentController, LifeCycleController {
  
  private boolean                 started          = false;
  
  /* bindings */
  protected final static String   eventOutItfName   = "event-out";
  protected EventHandler          eventOut;
  
  /* content */
  protected Set<Target> targets   = null;
  
  /* Attributes */
  protected final static String   timeoutAttName   = "timeout";
  protected int                   timeout          = 3000;
  
  protected final static String   frequencyAttName = "frequency";
  protected int                   frequency        = 1000;
  
  protected ScheduledExecutorService pingScheduler;
  
  public BasicPing(){
    pingScheduler = Executors.newScheduledThreadPool(10);
    targets = Collections.synchronizedSet(new HashSet<Target>());
  }
  
  /** ****************** PingResultHandler ********************** */
  
  public void sendFailureEvent(Component failedCmp_M){
    assert failedCmp_M != null;
    Event e = EventFactory.newEvent(EventTag.FAILURE);
    e.getEventBody().put("failedCmp_M", failedCmp_M);
    try {
      //log("Send failure event");
      eventOut.handleEvent(e);
    } catch (JadeException e1) {
      log(e1);
    }
  }
  
  /** ****************** generic attribute controller ********************** */
  @Override
  public String getAttribute(String name) throws NoSuchAttributeException {
    if (name.equals(timeoutAttName))
      return Integer.toString(timeout);
    else if (name.equals(frequencyAttName))
      return Integer.toString(frequency);
    else
      return super.getAttribute(name);
  }

  @Override
  public String[] listFcAtt() {
    String[] attList = new String[] { timeoutAttName, frequencyAttName };
    return super.concat(attList, super.listFcAtt());
  }
  
  @Override
  public void setAttribute(String name, String value) throws NoSuchAttributeException {
    if (name.equals(timeoutAttName))
      timeout = Integer.parseInt(value);
    else if (name.equals(frequencyAttName))
      frequency = Integer.parseInt(value);
    else
      super.setAttribute(name, value);
  }

  /** ****************** binding controller ********************** */
  @Override
  public String[] listFc() {
    String[] list = new String[]{eventOutItfName};
    return super.concat(list, super.listFc());
  }

  @Override
  public Object lookupFc(final String itfName) throws NoSuchInterfaceException {
    if (itfName.equals(eventOutItfName))
      return eventOut;
    else
      return super.lookupFc(itfName);
  }

  @Override
  public void bindFc(final String itfName, final Object itfValue) throws NoSuchInterfaceException,
      IllegalBindingException, IllegalLifeCycleException {
    if (itfName.equals(eventOutItfName)) 
      eventOut = (EventHandler) itfValue;
    else
      super.bindFc(itfName, itfValue);
  }

  @Override
  public void unbindFc(final String itfName) throws NoSuchInterfaceException, IllegalBindingException,
      IllegalLifeCycleException {
    if (itfName.equals(eventOutItfName)) 
      eventOut = null;
    else
      super.unbindFc(itfName);
  }

  /** ****************** Event Handler *************************** */
  
  public void handleEvent(Event e) throws JadeException {
    Component node_E = (Component)e.getEventBody().get("newNode");
    try {
      FractalUtil.getContentController(self).addFcSubComponent(node_E);
    } catch (IllegalContentException e1) {
      throw new JadeException("can't add node in the probe", e1);
    } catch (IllegalLifeCycleException e1) {
      throw new JadeException("can't add node in the probe", e1);
    }
  }

  /** ****************** Content Controller ********************** */

  public Object getFcInternalInterface(String arg0) throws NoSuchInterfaceException {
    throw new NoSuchInterfaceException(arg0);
  }

  public Object[] getFcInternalInterfaces() {
    return new Object[]{};
  }

  public Component[] getFcSubComponents() {
    Component[] subs = new Component[targets.size()];
    int i = 0;
    for(Target t : targets) subs[i++]=t.exec;
    return subs;
  }

  public void addFcSubComponent(Component cmp_E) throws IllegalContentException, IllegalLifeCycleException {
    log("Add "+FractalNavigator.getName(cmp_E)+" in probe");
    Target t = new Target();
    t.exec = cmp_E;
    t.meta = Reflex.getDualComponent(cmp_E);
    try {
      PingController pc = FractalUtil.getPingController(t.exec);
      t.pingTask = new PingTask(t.meta, pc.getHostName(), pc.getPort(), timeout, this);
    } catch (Exception e) {
      throw new IllegalContentException("Can't create PingTask :" + e.getMessage());
    }
    if(started)t.handler = pingScheduler.scheduleAtFixedRate(t.pingTask, frequency, frequency, TimeUnit.MILLISECONDS);
    targets.add(t);
  }
  
  public void removeFcSubComponent(Component cmp_E) throws IllegalContentException, IllegalLifeCycleException {
    log("Remove in probe");
    Target toRemove = null;
    for (Target t : targets){
      if(t.exec.equals(cmp_E)){
        toRemove = t;
        break;
      }
    }
    if(toRemove == null){
      // log("Component is not a sub component of probe");
      throw new IllegalContentException("Component is not a sub component of allocator");
    }else{
      toRemove.handler.cancel(true);
      targets.remove(toRemove);
    }
  }
  
  /** ****************** LCC Controller ********************** */
  
  public String getFcState() {
    return started?STARTED:STOPPED;
  }
  public void startFc() throws IllegalLifeCycleException {
    if(!started){
      started = true;
      for(Target t : targets){
        t.handler = pingScheduler.scheduleAtFixedRate(t.pingTask, frequency, frequency, TimeUnit.MILLISECONDS);
      }
    }
  }
  public void stopFc() throws IllegalLifeCycleException {
    if(started){
      started = false;
      for(Target t : targets){
        t.handler.cancel(true);
      }
    }
  }
  
  protected class Target {
    public Component meta;
    public Component exec;
    public PingTask pingTask;
    public ScheduledFuture<?> handler;
  }
}
